﻿using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using Sage.Caching.Memory.Extensions;
using Sage.Caching.Memory.Models;
using Sage.Caching.Memory.Options;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Sage.Caching.Memory.Services
{
    /// <summary>
    /// 内存缓存服务的实现，基于 IMemoryCache。
    /// 提供线程安全的缓存操作、区域支持以及统计功能。
    /// </summary>
    public sealed class MemoryCacheService : ICacheService
    {
        private readonly IMemoryCache _memoryCache;
        private readonly MemoryCacheServiceOptions _options;
        private readonly ConcurrentDictionary<string, ConcurrentHashSet<string>> _regionKeys = new();
        private CancellationTokenSource _resetCacheToken = new();
        private readonly CacheStats _stats = new();
        private readonly Timer? _statsTimer;
        private bool _disposed;

        /// <summary>
        /// 构造函数，初始化 MemoryCacheService。
        /// </summary>
        /// <param name="memoryCache">IMemoryCache 实例。</param>
        /// <param name="options">MemoryCacheServiceOptions 实例，用于配置缓存功能。</param>
        /// <exception cref="ArgumentNullException">参数为 null 时抛出。</exception>
        public MemoryCacheService(IMemoryCache memoryCache, MemoryCacheServiceOptions options)
        {
            _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
            _options = options ?? throw new ArgumentNullException(nameof(options));

            // 如果启用了统计，创建定期刷新统计信息的定时器
            if (_options.EnableStats)
            {
                _statsTimer = new Timer(
                    _ => { _stats.LastUpdated = DateTime.UtcNow; },
                    null,
                    _options.StatsFlushInterval,
                    _options.StatsFlushInterval);
            }
        }

        /// <summary>
        /// 尝试从缓存中获取指定键的值。
        /// </summary>
        /// <typeparam name="T">缓存项的类型。</typeparam>
        /// <param name="key">缓存的键名称。</param>
        /// <param name="value">获取的值。若键不存在，则返回默认值。</param>
        /// <returns>是否找到缓存项。</returns>
        public bool TryGetValue<T>(string key, out T? value)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(key);

            // 应用键前缀（如果有）
            string actualKey = ApplyKeyPrefix(key);

            // 尝试从缓存获取值
            if (_memoryCache.TryGetValue(actualKey, out var cachedValue))
            {
                // 如果启用了类型检查，确保缓存项类型与请求的类型匹配
                if (_options.EnableTypeChecking && cachedValue is not T)
                {
                    value = default;
                    UpdateStatsMiss();
                    return false;
                }

                // 类型匹配，设置输出值并更新命中统计
                value = (T?)cachedValue;
                UpdateStatsHit();
                return true;
            }

            // 缓存中未找到指定键
            value = default;
            UpdateStatsMiss();
            return false;
        }

        /// <summary>
        /// 设置缓存项到内存缓存中。
        /// </summary>
        /// <typeparam name="T">缓存项的类型。</typeparam>
        /// <param name="key">缓存的键名称。</param>
        /// <param name="value">缓存的值。</param>
        /// <param name="region">区域名称（可选），用于分组键。</param>
        /// <param name="options">缓存项的配置选项（可选）。</param>
        /// <exception cref="ArgumentNullException">键为 null 时抛出。</exception>
        public void Set<T>(string key, T value, string? region = null, MemoryCacheEntryOptions? options = null)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(key);

            // 应用键前缀（如果有）
            string actualKey = ApplyKeyPrefix(key);

            // 使用默认选项（如果未提供），并创建副本以避免修改原始选项
            var entryOptions = options != null
                ? options.Clone()
                : _options.DefaultEntryOptions.Clone();

            // 设置缓存项过期回调
            entryOptions.RegisterPostEvictionCallback(OnCacheEntryRemoved);

            // 设置缓存项过期源（用于整体清除）
            var expirationToken = new CancellationChangeToken(_resetCacheToken.Token);
            entryOptions.AddExpirationToken(expirationToken);

            // 设置缓存项
            _memoryCache.Set(actualKey, value, entryOptions);

            // 如果提供了区域，将键添加到区域索引中
            if (!string.IsNullOrEmpty(region))
            {
                var keys = _regionKeys.GetOrAdd(region, _ => []);
                keys.Add(actualKey);
            }

            // 更新统计信息
            UpdateStatsItemAdded();
        }

        /// <summary>
        /// 从缓存中移除指定键的值。
        /// </summary>
        /// <param name="key">要移除的键名称。</param>
        /// <param name="region">区域。</param>
        /// <exception cref="ArgumentNullException">键为 null 时抛出。</exception>
        public void Remove(string key, string? region = null)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(key);
            // 应用键前缀（如果有）
            string actualKey = ApplyKeyPrefix(key);
            // 从缓存中移除项
            _memoryCache.Remove(actualKey);

            if (region != null)
            {
                // 只从指定区域移除键
                if (_regionKeys.TryGetValue(region, out var keys))
                {
                    keys.TryRemove(actualKey);
                }
            }
            else
            {
                // 从所有区域移除（与原Remove行为一致）
                RemoveKeyFromAllRegions(actualKey);
            }
            // 更新统计信息
            UpdateStatsItemRemoved();
        }


        /// <summary>
        /// 获取一项缓存，如果不存在则进行创建。
        /// </summary>
        /// <typeparam name="T">缓存项的类型。</typeparam>
        /// <param name="key">缓存项的键。</param>
        /// <param name="valueFactory">创建缓存值的工厂方法。</param>
        /// <param name="region">区域名称（用于逻辑分组）。</param>
        /// <param name="options">缓存项的配置选项。如果未指定则使用默认配置。</param>
        /// <returns>缓存中的值或新创建的值。</returns>
        /// <exception cref="ArgumentNullException">键或值工厂为 null 时抛出。</exception>
        public T? GetOrCreate<T>(string key, Func<T> valueFactory, string? region = null, MemoryCacheEntryOptions? options = null)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(key);
            ArgumentNullException.ThrowIfNull(valueFactory);

            // 应用键前缀（如果有）
            string actualKey = ApplyKeyPrefix(key);

            // 先尝试从缓存获取，使用actualKey
            if (TryGetValue<T>(actualKey, out var value))
            {
                return value;
            }

            try
            {
                // 创建新的缓存项
                value = valueFactory();

                // 设置到缓存，使用actualKey保持一致
                Set(actualKey, value, region, options);

                return value;
            }
            catch (Exception ex)
            {
                // 根据配置决定是否抛出异常
                if (_options.ThrowOnCreateError)
                {
                    throw;
                }

                Debug.WriteLine($"Error creating cache item for key '{actualKey}': {ex.Message}");
                return default;
            }
        }

        /// <summary>
        /// 异步获取一项缓存，如果不存在则异步创建。
        /// </summary>
        /// <typeparam name="T">缓存项的类型。</typeparam>
        /// <param name="key">缓存项的键。</param>
        /// <param name="valueFactory">异步创建缓存值的工厂方法。</param>
        /// <param name="region">区域名称（用于逻辑分组）。</param>
        /// <param name="options">缓存项的配置选项。如果未指定则使用默认配置。</param>
        /// <returns>包含缓存值的任务。</returns>
        /// <exception cref="ArgumentNullException">键或值工厂为 null 时抛出。</exception>
        public async ValueTask<T?> GetOrCreateAsync<T>(string key, Func<ValueTask<T>> valueFactory, string? region = null, MemoryCacheEntryOptions? options = null)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(key);
            ArgumentNullException.ThrowIfNull(valueFactory);

            // 应用键前缀（如果有）
            string actualKey = ApplyKeyPrefix(key);

            // 先尝试从缓存获取，使用actualKey保持一致
            if (TryGetValue<T>(actualKey, out var value))
            {
                return value;
            }

            try
            {
                // 异步创建新的缓存项
                value = await valueFactory();

                // 设置到缓存，使用actualKey保持一致
                Set(actualKey, value, region, options);

                return value;
            }
            catch (Exception ex)
            {
                // 根据配置决定是否抛出异常
                if (_options.ThrowOnCreateError)
                {
                    throw;
                }

                Debug.WriteLine($"Error creating cache item for key '{actualKey}': {ex.Message}");
                return default;
            }

        }

        /// <summary>
        /// 移除所有缓存项。
        /// </summary>
        public void Clear()
        {
            // 通过取消令牌触发所有缓存项的过期
            var oldTokenSource = _resetCacheToken;
            oldTokenSource.Cancel();
            oldTokenSource.Dispose();

            // 创建新的令牌源
            var newTokenSource = new CancellationTokenSource();
            _resetCacheToken = newTokenSource;

            // 清空区域索引
            _regionKeys.Clear();

            // 更新统计信息
            if (_options.EnableStats)
            {
                _stats._totalItems = 0;
            }
        }

        /// <summary>
        /// 清除指定的区域中所有缓存项。
        /// </summary>
        /// <param name="region">区域名称。</param>
        /// <returns>被移除的项数量。</returns>
        /// <exception cref="ArgumentNullException">区域为 null 时抛出。</exception>
        public int ClearRegion(string region)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(region);

            // 如果区域不存在，返回0
            if (!_regionKeys.TryRemove(region, out var keys))
            {
                return 0;
            }

            int count = 0;

            // 移除区域中的所有缓存项
            foreach (var key in keys)
            {
                _memoryCache.Remove(key);
                count++;
            }

            // 更新统计信息
            if (_options.EnableStats)
            {
                Interlocked.Add(ref _stats._removedItems, count);
            }

            return count;
        }

        /// <summary>
        /// 批量设置多个缓存项。
        /// </summary>
        /// <typeparam name="T">缓存项的类型。</typeparam>
        /// <param name="items">要设置的键值对。</param>
        /// <param name="region">区域名称（用于逻辑分组）。</param>
        /// <param name="options">缓存项的配置选项。如果未指定则使用默认配置。</param>
        /// <exception cref="ArgumentNullException">项集合为 null 时抛出。</exception>
        public void SetMany<T>(IEnumerable<KeyValuePair<string, T>> items, string? region = null, MemoryCacheEntryOptions? options = null)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(items);

            foreach (var item in items)
            {
                Set(item.Key, item.Value, region, options);
            }
        }

        /// <summary>
        /// 批量移除多个缓存项。
        /// </summary>
        /// <param name="keys">要移除的键集合。</param>
        /// <returns>实际移除的项数量。</returns>
        /// <exception cref="ArgumentNullException">键集合为 null 时抛出。</exception>
        public int RemoveMany(IEnumerable<string> keys)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(keys);

            int count = 0;

            foreach (var key in keys)
            {
                if (key != null)
                {
                    Remove(key);
                    count++;
                }
            }

            return count;
        }

        /// <summary>
        /// 获取缓存的统计信息。
        /// </summary>
        /// <returns>缓存统计信息对象。</returns>
        public CacheStats GetCacheStats()
        {
            // 如果未启用统计，则只返回项目总数
            if (!_options.EnableStats)
            {
                return new CacheStats
                {
                    _totalItems = GetTotalItemCount(),
                    LastUpdated = DateTime.UtcNow
                };
            }

            // 创建并返回统计信息快照
            return _stats.CreateSnapshot();
        }

        /// <summary>
        /// 获取指定区域中的所有键。
        /// </summary>
        /// <param name="region">区域名称。</param>
        /// <returns>区域中的键集合。</returns>
        /// <exception cref="ArgumentNullException">区域为 null 时抛出。</exception>
        public IReadOnlyCollection<string> GetRegionKeys(string region)
        {
            // 参数验证
            ArgumentNullException.ThrowIfNull(region);

            // 如果区域存在，返回其键集合；否则返回空集合
            return _regionKeys.TryGetValue(region, out var keys)
                ? [.. keys]
                : Array.Empty<string>();
        }

        /// <summary>
        /// 释放当前服务使用的资源。
        /// </summary>
        public void Dispose()
        {
            if (!_disposed)
            {
                // 释放重置缓存令牌
                _resetCacheToken.Dispose();

                // 释放统计定时器
                _statsTimer?.Dispose();

                // 标记为已释放
                _disposed = true;

                // 抑制终结器
                GC.SuppressFinalize(this);
            }
        }

        #region 私有辅助方法

        /// <summary>
        /// 缓存项被移除时的回调方法。
        /// </summary>
        private void OnCacheEntryRemoved(object key, object? _, EvictionReason reason, object? __)
        {

            try
            {
                // 更新统计信息
                if (_options.EnableStats)
                {
                    Interlocked.Decrement(ref _stats._totalItems);

                    // 如果是由于过期或内存压力导致的移除，增加逐出计数
                    if (reason == EvictionReason.Expired || reason == EvictionReason.Capacity)
                    {
                        Interlocked.Increment(ref _stats._evictedItems);
                    }
                }

                // 确保键是字符串类型，并且不为null
                if (key is string stringKey && !string.IsNullOrEmpty(stringKey))
                {
                    RemoveKeyFromAllRegions(stringKey);
                }
                else if (key is not null)
                {
                    // 记录非字符串键的警告，这通常不应该发生
                    Console.WriteLine($"Warning: Non-string key detected in OnCacheEntryRemoved: {key.GetType().Name}");
                }
            }
            catch (Exception ex)
            {
                // 捕获并记录任何可能发生的异常，避免影响缓存系统
                Console.WriteLine($"Error in OnCacheEntryRemoved: {ex.Message}");
            }
            //if (_options.EnableStats)
            //{
            //    // 更新统计信息
            //    Interlocked.Decrement(ref _stats._totalItems);

            //    // 如果是由于过期或内存压力导致的移除，增加逐出计数
            //    if (reason == EvictionReason.Expired || reason == EvictionReason.Capacity)
            //    {
            //        Interlocked.Increment(ref _stats._evictedItems);
            //    }
            //}

            //// 如果键是字符串，从所有区域中移除
            //if (key is string stringKey)
            //{
            //    RemoveKeyFromAllRegions(stringKey);
            //}
        }

        /// <summary>
        /// 从所有区域中移除指定的键。
        /// </summary>
        /// <param name="key">要移除的键。</param>
        private void RemoveKeyFromAllRegions(string key)
        {
            foreach (var regionKeySet in _regionKeys.Values)
            {
                regionKeySet.TryRemove(key);
            }
        }

        /// <summary>
        /// 获取缓存中项目的总数。
        /// </summary>
        /// <returns>缓存项总数。</returns>
        private long GetTotalItemCount()
        {
            // 计算所有区域键集合的大小总和
            return _regionKeys.Values.Sum(keys => keys.Count);
        }

        /// <summary>
        /// 应用键前缀（如果已配置）。
        /// </summary>
        /// <param name="key">原始键。</param>
        /// <returns>应用前缀后的键。</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private string ApplyKeyPrefix(string key)
        {
            return string.IsNullOrEmpty(_options.KeyPrefix) ? key : $"{_options.KeyPrefix}:{key}";
        }

        /// <summary>
        /// 更新缓存命中统计。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void UpdateStatsHit()
        {
            if (_options.EnableStats)
            {
                Interlocked.Increment(ref _stats._hits);
            }
        }

        /// <summary>
        /// 更新缓存未命中统计。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void UpdateStatsMiss()
        {
            if (_options.EnableStats)
            {
                Interlocked.Increment(ref _stats._misses);
            }
        }

        /// <summary>
        /// 更新缓存项添加统计。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void UpdateStatsItemAdded()
        {
            if (_options.EnableStats)
            {
                Interlocked.Increment(ref _stats._totalItems);
                Interlocked.Increment(ref _stats._addedItems);
            }
        }

        /// <summary>
        /// 更新缓存项移除统计。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void UpdateStatsItemRemoved()
        {
            if (_options.EnableStats)
            {
                Interlocked.Increment(ref _stats._removedItems);
            }
        }

        #endregion
    }

    /// <summary>
    /// 表示一个线程安全的哈希集合。
    /// </summary>
    /// <typeparam name="T">集合元素的类型。</typeparam>
    internal sealed class ConcurrentHashSet<T> : IEnumerable<T> where T : notnull
    {
        private readonly ConcurrentDictionary<T, byte> _dictionary = new();

        /// <summary>
        /// 获取集合中元素的数量。
        /// </summary>
        public int Count => _dictionary.Count;

        /// <summary>
        /// 尝试将元素添加到集合中。
        /// </summary>
        /// <param name="item">要添加的元素。</param>
        /// <returns>如果元素添加成功，则为 true；如果元素已存在，则为 false。</returns>
        public bool Add(T item) => _dictionary.TryAdd(item, 0);

        /// <summary>
        /// 检查集合是否包含指定元素。
        /// </summary>
        /// <param name="item">要检查的元素。</param>
        /// <returns>如果集合包含指定元素，则为 true；否则为 false。</returns>
        public bool Contains(T item) => _dictionary.ContainsKey(item);

        /// <summary>
        /// 尝试从集合中移除元素。
        /// </summary>
        /// <param name="item">要移除的元素。</param>
        /// <returns>如果元素移除成功，则为 true；否则为 false。</returns>
        public bool TryRemove(T item) => _dictionary.TryRemove(item, out _);

        /// <summary>
        /// 清空集合中的所有元素。
        /// </summary>
        public void Clear() => _dictionary.Clear();

        /// <summary>
        /// 返回一个循环访问集合的枚举器。
        /// </summary>
        /// <returns>可用于循环访问集合的枚举器。</returns>
        public IEnumerator<T> GetEnumerator() => _dictionary.Keys.GetEnumerator();

        /// <summary>
        /// 返回一个循环访问集合的枚举器。
        /// </summary>
        /// <returns>可用于循环访问集合的枚举器。</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
    }
}
